package com.sec.android.app.myfiles.feature.detailinfo;

import android.content.Context;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.text.TextUtils;
import android.view.View;
import android.widget.TextView;

import com.sec.android.app.myfiles.R;
import com.sec.android.app.myfiles.module.abstraction.FileRecord;
import com.sec.android.app.myfiles.module.local.file.LocalFileRecord;
import com.sec.android.app.myfiles.module.preview.CompressFileRecord;
import com.sec.android.app.myfiles.util.PreferenceUtils;
import com.sec.android.app.myfiles.util.SemFwWrapper;
import com.sec.android.app.myfiles.util.UiUtils;

import java.io.File;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * Created by daesu83.kim on 2017-03-15.
 */

public class ChildCountLoaderImp extends AbsDetailsInfoLoaderImp {

    private static final int MAX_THREAD = Runtime.getRuntime().availableProcessors() / 2;
    private static final int MAX_CACHE_SIZE = 2048;
    private static final int REMOVE_TARGET_HIT_COUNT = 10;
    private static final String LOAD_THREAD_NAME = "load_thread";

    private HandlerThread[] mLoadThread;
    private LoadHandler[] mLoadHandler;
    private Handler mUiUpdateHandler = new UiUpdateHandler();
    private int mCurThreadIndex = 0;
    private HashMap<String, LoadResult> mChildWithHiddenCountCache;
    private HashMap<String, LoadResult> mChildCountCache;


    public ChildCountLoaderImp() {
        mLoadThread = new HandlerThread[MAX_THREAD];
        mLoadHandler = new LoadHandler[MAX_THREAD];
        mChildWithHiddenCountCache = new HashMap<>();
        mChildCountCache = new HashMap<>();

        for (int i = 0; i < MAX_THREAD; i++) {
            mLoadThread[i] = new HandlerThread(LOAD_THREAD_NAME + i);
            mLoadThread[i].start();

            Looper looper = mLoadThread[i].getLooper();

            if (looper != null) {
                mLoadHandler[i] = new LoadHandler(looper);
            }
        }
    }

    @Override
    public void loadDetailsInfo(Context context, FileRecord record, View view) {
        if (context != null && record != null && view instanceof TextView) {
            if (record.isDirectory()) {
                LoadRequestInfo req = new LoadRequestInfo();
                req.mRecord = record;
                view.setTag(record.getFullPath());
                req.mTargetView = (TextView) view;

                int childCount = getChildCountFromCache(context, record);
                TextView childCountTextView = (TextView) view;
                if (childCount == -1) {
                    childCountTextView.setText(context.getString(R.string.calculating_child_count));
                    mLoadHandler[mCurThreadIndex].sendMessageAtFrontOfQueue(mLoadHandler[mCurThreadIndex].obtainMessage(0, req));
                    mCurThreadIndex++;
                    if (mCurThreadIndex >= MAX_THREAD) {
                        mCurThreadIndex = 0;
                    }
                } else {
                    childCountTextView.setText(UiUtils.makeItemsString(context, childCount));
                }
            } else {
                view.setTag(null);
            }
        }
    }

    private int getChildCountFromCache(Context context, FileRecord record) {
        int ret = -1;
        boolean showHidden = PreferenceUtils.getShowHiddenFiles(context);
        String fullPath = record.getFullPath();
        LoadResult loadResult;
        Map<String, LoadResult> cache;

        if (showHidden) {
            cache = mChildWithHiddenCountCache;
        } else {
            cache = mChildCountCache;
        }

        loadResult = cache.get(fullPath);

        if (loadResult != null) {
            if (loadResult.mLastModified == record.getDate()) {
                ret = loadResult.mChildCount;
                record.setItemCount(ret, showHidden);
                loadResult.mHitCount++;
            } else {
                cache.remove(fullPath);
            }
        }
        return ret;
    }

    private void addResultToCache(Context context, FileRecord record, LoadResult result) {
        boolean showHidden = PreferenceUtils.getShowHiddenFiles(context);
        Map<String, LoadResult> cache;
        synchronized (ChildCountLoaderImp.class) {
            if (showHidden) {
                cache = mChildWithHiddenCountCache;
            } else {
                cache = mChildCountCache;
            }

            if (cache.size() > MAX_CACHE_SIZE) {
                Iterator<Map.Entry<String, LoadResult>> it = cache.entrySet().iterator();
                Map.Entry<String, LoadResult> entry;
                LoadResult r;
                while (it.hasNext()) {
                    entry = it.next();
                    r = entry.getValue();
                    if (r == null || r.mHitCount < REMOVE_TARGET_HIT_COUNT) {
                        it.remove();
                    }
                }
            }
            cache.put(record.getFullPath(), result);
            record.setItemCount(result.mChildCount, showHidden);
        }
    }

    @Override
    public void clearCache(FileRecord fileRecord) {
        if (fileRecord != null) {
            mChildCountCache.remove(fileRecord.getFullPath());
        }
    }

    private static class LoadRequestInfo {
        public FileRecord mRecord;
        public TextView mTargetView;
    }

    private static class LoadResult {
        public int mHitCount;
        public long mLastModified;
        public int mChildCount;
        public String mPath;
    }

    private final class LoadHandler extends Handler {

        public LoadHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            LoadRequestInfo info = (LoadRequestInfo) msg.obj;
            LoadResult result = new LoadResult();
            String fullPath = info.mRecord.getFullPath();
            File file = SemFwWrapper.file(fullPath);
            Context context = info.mTargetView.getContext();
            boolean showHidden = PreferenceUtils.getShowHiddenFiles(context);
            FileRecord.StorageType storageType = info.mRecord.getStorageType();

            switch (storageType) {
                case Compress:
                    file = SemFwWrapper.file(((CompressFileRecord) info.mRecord).getArchiveRecord().getFullPath());
                    result.mChildCount = info.mRecord.getItemCount(showHidden);
                    break;
                default:
                    int childCount[] = LocalFileRecord.getChildFileCount(file);
                    result.mChildCount = showHidden ? childCount[0] : childCount[1];
                    break;
            }
            result.mLastModified = (storageType == FileRecord.StorageType.Cloud ? info.mRecord.getDate() : file.lastModified());
            addResultToCache(context, info.mRecord, result);

            Message message = mUiUpdateHandler.obtainMessage(0, info);
            message.arg1 = result.mChildCount;
            mUiUpdateHandler.sendMessageAtFrontOfQueue(message);
        }
    }

    private final static class UiUpdateHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            LoadRequestInfo info = (LoadRequestInfo) msg.obj;
            String curPath = (String) info.mTargetView.getTag();
            String targetPath = info.mRecord.getFullPath();
            if (!TextUtils.isEmpty(curPath) && !TextUtils.isEmpty(targetPath) &&
                    curPath.equals(targetPath)) {
                info.mTargetView.setText(UiUtils.makeItemsString(info.mTargetView.getContext(), msg.arg1));
            }
            msg.obj = null;
            info.mTargetView = null;
            info.mRecord = null;
        }
    }
}
